<!DOCTYPE html>
<!--
Copyright (c) 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/extras/chrome/cc/input_latency_async_slice.html">
<link rel="import" href="/tracing/extras/chrome/chrome_test_utils.html">
<link rel="import" href="/tracing/extras/chrome/event_finder_utils.html">
<link rel="import" href="/tracing/importer/user_expectation_verifier.html">

<script>
'use strict';

tr.b.unittest.testSuite(function() {
  const INPUT_TYPE = tr.e.cc.INPUT_EVENT_TYPE_NAMES;
  const ChromeTestUtils = tr.e.chrome.ChromeTestUtils;
  const UserExpectationVerifier = tr.importer.UserExpectationVerifier;

  function addFrameEventForInput(model, event) {
    const frame = ChromeTestUtils.addFrameEvent(model,
        {start: event.start, end: event.end, isTopLevel: true});
    model.flowEvents.push(tr.c.TestUtils.newFlowEventEx({
      id: event.id,
      start: event.start,
      end: event.end,
      startSlice: frame,
      endSlice: frame
    }));
  }

  test('empty', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
    };
    verifier.expectedUEs = [];
    verifier.expectedSegments = [];
    verifier.verify();
  });

  test('vrExpectations', function() {
    const verifier = new UserExpectationVerifier();

    verifier.customizeModelCallback = function(model) {
      model.gpuProcess = model.getOrCreateProcess(3);
      model.gpuMain = model.gpuProcess.getOrCreateThread(6);
      model.gpuMain.name = 'CrGpuMain';

      const series = new tr.model.CounterSeries('gpu.WebVR FPS');
      series.addCounterSample(0, 1);
      series.addCounterSample(990, 2);
      series.addCounterSample(1005, 3);
      series.addCounterSample(1500, 4);
      model.gpuProcess.getOrCreateCounter('gpu',
          'WebVR FPS').addSeries(series);

      model.gpuMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx(
          {title: 'Vr.DrawFrame', start: 5, end: 10,
            type: tr.model.ThreadSlice}));
      model.gpuMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx(
          {title: 'Vr.DrawFrame', start: 995, end: 1000,
            type: tr.model.ThreadSlice}));
      model.gpuMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx(
          {title: 'Vr.DrawFrame', start: 1010, end: 1050,
            type: tr.model.ThreadSlice}));
    };

    verifier.expectedUEs = [
      {title: 'VR Response', start: 0, end: 1000, eventCount: 4},
      {title: 'VR Animation', start: 1000, end: 1500, eventCount: 4},
    ];

    verifier.expectedSegments = [
      {start: 0, end: 1000, expectations: [verifier.expectedUEs[0]]},
      {start: 1000, end: 1500, expectations: [verifier.expectedUEs[1]]},
    ];

    verifier.verify();
  });

  test('videoExpectations_gapInMiddle', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 0, end: 100, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20});
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 200, end: 300, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 210, end: 220});
    };
    verifier.expectedUEs = [
      {title: 'Video Animation', start: 0, end: 100, eventCount: 2},
      {title: 'Idle', start: 100, end: 200, eventCount: 0},
      {title: 'Video Animation', start: 200, end: 300, eventCount: 2},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 200, expectations: [verifier.expectedUEs[1]]},
      {start: 200, end: 300, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('videoExpectations_overlapping', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 0, end: 200, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20});
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 100, end: 300, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 210, end: 220});
    };
    verifier.expectedUEs = [
      {title: 'Video Animation', start: 0, end: 300, eventCount: 4},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 300, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('videoExpectations_oneInTheOther', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 0, end: 300, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20});
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 100, end: 200, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 110, end: 120});
    };
    verifier.expectedUEs = [
      {title: 'Video Animation', start: 0, end: 300, eventCount: 4},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 300, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('videoExpectations_dontMergeWithOtherAnimations', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = model => {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'VideoPlayback', start: 0, end: 100, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20}),
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'Animation', start: 90, end: 190, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 110, end: 120});
    };
    verifier.expectedUEs = [
      {title: 'Video Animation', start: 0, end: 100, eventCount: 2},
      {title: 'CSS Animation', start: 90, end: 190, eventCount: 2},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 90, expectations: [verifier.expectedUEs[0]]},
      {
        start: 90,
        end: 100,
        expectations: [verifier.expectedUEs[0], verifier.expectedUEs[1]]
      },
      {start: 100, end: 190, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('slowMouseMoveResponses', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_DOWN, {start: 0, end: 10});
      let mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 10, end: 20, id: '0x100'});
      addFrameEventForInput(model, mouseMove);

      mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 70, end: 80, id: '0x101'});
      addFrameEventForInput(model, mouseMove);

      mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 130, end: 140, id: '0x102'});
      addFrameEventForInput(model, mouseMove);
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 10, eventCount: 0},
      {title: 'Mouse Response', start: 10, end: 20, eventCount: 4},
      {title: 'Idle', start: 20, end: 70, eventCount: 0},
      {title: 'Mouse Response', start: 70, end: 80, eventCount: 3},
      {title: 'Idle', start: 80, end: 130, eventCount: 0},
      {title: 'Mouse Response', start: 130, end: 140, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 10, expectations: [verifier.expectedUEs[0]]},
      {start: 10, end: 20, expectations: [verifier.expectedUEs[1]]},
      {start: 20, end: 70, expectations: [verifier.expectedUEs[2]]},
      {start: 70, end: 80, expectations: [verifier.expectedUEs[3]]},
      {start: 80, end: 130, expectations: [verifier.expectedUEs[4]]},
      {start: 130, end: 140, expectations: [verifier.expectedUEs[5]]}
    ];
    verifier.verify();
  });

  test('mouseEventResponses', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      const mouseDown = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_DOWN, {start: 0, end: 50, id: '0x100'});
      addFrameEventForInput(model, mouseDown);

      const mouseUp = ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_UP,
          {start: 50, end: 100, id: '0x101'});
      addFrameEventForInput(model, mouseUp);

      const mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 200, end: 250, id: '0x102'});
      addFrameEventForInput(model, mouseMove);
    };
    verifier.expectedUEs = [
      {title: 'Mouse Response', start: 0, end: 50, eventCount: 3},
      {title: 'Mouse Response', start: 50, end: 100, eventCount: 3},
      {title: 'Idle', start: 100, end: 200, eventCount: 0},
      {title: 'Mouse Response', start: 200, end: 250, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 50, expectations: [verifier.expectedUEs[0]]},
      {start: 50, end: 100, expectations: [verifier.expectedUEs[1]]},
      {start: 100, end: 200, expectations: [verifier.expectedUEs[2]]},
      {start: 200, end: 250, expectations: [verifier.expectedUEs[3]]}
    ];
    verifier.verify();
  });

  test('mouseEventsIgnored', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_MOVE,
          {start: 0, end: 50});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_DOWN,
          {start: 50, end: 100});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 100, eventCount: 0}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]}
    ];
    verifier.verify();
  });

  test('unassociatedEvents', function() {
    // Unassociated ThreadSlices that start during an Idle should be associated
    // with it. Expect the IdleExpectation to have 2 associated events: both of
    // the ThreadSlices in the model.
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      const start = tr.c.TestUtils.newSliceEx(
          {title: 'model start', start: 0, end: 1, type: tr.model.ThreadSlice});
      start.isTopLevel = true;
      model.browserMain.sliceGroup.pushSlice(start);

      const end = tr.c.TestUtils.newSliceEx(
          {title: 'model end', start: 9, end: 10, type: tr.model.ThreadSlice});
      end.isTopLevel = true;
      model.browserMain.sliceGroup.pushSlice(end);
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 10, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 10, expectations: [verifier.expectedUEs[0]]}
    ];
    verifier.verify();
  });

  test('stillLoading', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererProcess.objects.addSnapshot('ptr', 'loading', 'FrameLoader',
        25, { isOutermostMainFrame: true, isLoadingMainFrame: true,
              frame: {id_ref: '0xdeadbeef'},
              documentLoaderURL: 'http://example.com'});
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'navigationStart',
        start: 11,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      ChromeTestUtils.addFirstContentfulPaintEvent(model, {start: 20});
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstMeaningfulPaintCandidate',
        start: 30,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'domContentLoadedEventEnd',
        start: 40,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      ChromeTestUtils.addFrameEvent(model, {start: 100, end: 130});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 11, eventCount: 1},
      {title: 'Successful Load', start: 11, end: 130, eventCount: 4}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 11, expectations: [verifier.expectedUEs[0]]},
      {start: 11, end: 130, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('overlappingIdleAndLoadCollectUnassociatedEvents', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
      model.rendererProcess.objects.addSnapshot('ptr', 'loading', 'FrameLoader',
        15, { isOutermostMainFrame: true, isLoadingMainFrame: true,
              frame: {id_ref: '0xdeadbeef'},
              documentLoaderURL: 'http://example.com'});
      ChromeTestUtils.addFrameEvent(model, {start: 20, end: 40});
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'navigationStart',
        start: 20,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newSliceEx({
        cat: 'disabled-by-default-network',
        title: 'ResourceLoad',
        start: 20,
        duration: 5.0,
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstContentfulPaint',
        start: 20,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstMeaningfulPaintCandidate',
        start: 30,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'domContentLoadedEventEnd',
        start: 40,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      ChromeTestUtils.addFrameEvent(model, {start: 5000, end: 5050});
      // 3 Idle events.
      ChromeTestUtils.addRenderingEvent(model, {start: 5, end: 15});
      ChromeTestUtils.addRenderingEvent(model, {start: 11, end: 15});
      ChromeTestUtils.addRenderingEvent(model, {start: 13, end: 15});
      // 1 Idle event.
      ChromeTestUtils.addRenderingEvent(model, {start: 5045, end: 5046});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 20, eventCount: 4},
      {title: 'Successful Load', start: 20, end: 40, eventCount: 5},
      {title: 'Idle', start: 40, end: 5050, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 20, expectations: [verifier.expectedUEs[0]]},
      {start: 20, end: 40, expectations: [verifier.expectedUEs[1]]},
      {start: 40, end: 5050, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('flingFlingFling', function() {
    // This trace gave me so many different kinds of trouble that I'm just going
    // to copy it straight in here, without trying to clarify it at all.
    // measurmt-traces/mobile/cnet_fling_up_fling_down_motox_2013.json
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 919, end: 998});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_CANCEL,
          {start: 919, end: 1001});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_DOWN,
          {start: 919, end: 1001});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_CANCEL,
          {start: 974, end: 1020});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 974, end: 1020});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 974, end: 1040});
      ChromeTestUtils.addFrameEvent(model,
          {start: 1039, end: 1040, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 974, end: 1054});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 990, end: 1021});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 990, end: 1052});
      ChromeTestUtils.addFrameEvent(model,
          {start: 1051, end: 1052, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1006, end: 1021});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1022, end: 1036});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1022, end: 1052});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1038, end: 1049});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1038, end: 1068});
      ChromeTestUtils.addFrameEvent(model,
          {start: 1067, end: 1068, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 1046, end: 1050});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 1046, end: 1077});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 1432, end: 2238});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_CANCEL,
          {start: 1432, end: 2241});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1516, end: 2605});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 1532, end: 2274});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1532, end: 2294});
      ChromeTestUtils.addFrameEvent(model,
          {start: 2293, end: 2294, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1549, end: 2310});
      ChromeTestUtils.addFrameEvent(model,
          {start: 2309, end: 2310, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 1627, end: 2275});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 1627, end: 2310});
      ChromeTestUtils.addFrameEvent(model, {start: 2990, end: 3000});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 919, eventCount: 1},
      {title: 'Scroll Response', start: 919, end: 1054,
        eventCount: 6, isAnimationBegin: true},
      {title: 'Scroll Animation', start: 1054, end: 1068,
        eventCount: 9},
      {title: 'Fling Animation', start: 1054, end: 1432,
        eventCount: 3},
      {title: 'Scroll Response', start: 1432, end: 2605,
        eventCount: 5, isAnimationBegin: true},
      {title: 'Scroll Animation', start: 1549, end: 2310,
        eventCount: 3},
      {title: 'Fling Animation', start: 2605, end: 3000,
        eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 919, expectations: [verifier.expectedUEs[0]]},
      {start: 919, end: 1054, expectations: [verifier.expectedUEs[1]]},
      {start: 1054, end: 1068, expectations: [
        verifier.expectedUEs[2],
        verifier.expectedUEs[3],
      ]},
      {start: 1068, end: 1432, expectations: [verifier.expectedUEs[3]]},
      {start: 1432, end: 1549, expectations: [verifier.expectedUEs[4]]},
      {start: 1549, end: 2310, expectations: [
        verifier.expectedUEs[4],
        verifier.expectedUEs[5],
      ]},
      {start: 2310, end: 2605, expectations: [verifier.expectedUEs[4]]},
      {start: 2605, end: 3000, expectations: [verifier.expectedUEs[6]]},
    ];
    verifier.verify();
  });

  test('keyboardEvents', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.KEY_DOWN_RAW,
          {start: 0, end: 45});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.CHAR,
          {start: 10, end: 50});
    };
    verifier.expectedUEs = [
      {title: 'Keyboard Response', start: 0, end: 50, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 50, expectations: [verifier.expectedUEs[0]]}
    ];
    verifier.verify();
  });

  test('mouseResponses', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.CLICK,
          {start: 0, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.CONTEXT_MENU,
          {start: 200, end: 300});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 400, end: 500});
    };
    verifier.expectedUEs = [
      {title: 'Mouse Response', start: 0, end: 100, eventCount: 1},
      {title: 'Idle', start: 100, end: 200, eventCount: 0},
      {title: 'Mouse Response', start: 200, end: 300, eventCount: 1},
      {title: 'Idle', start: 300, end: 400, eventCount: 0},
      {title: 'MouseWheel Response', start: 400, end: 500,
        eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 200, expectations: [verifier.expectedUEs[1]]},
      {start: 200, end: 300, expectations: [verifier.expectedUEs[2]]},
      {start: 300, end: 400, expectations: [verifier.expectedUEs[3]]},
      {start: 400, end: 500, expectations: [verifier.expectedUEs[4]]},
    ];
    verifier.verify();
  });

  test('mouseWheelAnimation', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 0, end: 20});
      ChromeTestUtils.addFrameEvent(model,
          {start: 19, end: 20, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 16, end: 36});
      ChromeTestUtils.addFrameEvent(model,
          {start: 35, end: 36, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 55, end: 75});
      ChromeTestUtils.addFrameEvent(model,
          {start: 74, end: 75, isTopLevel: true});

      // This threshold uses both events' start times, not end...start.
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 100, end: 150});
      ChromeTestUtils.addFrameEvent(model,
          {start: 149, end: 150, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 141, end: 191});
      ChromeTestUtils.addFrameEvent(model,
          {start: 190, end: 191, isTopLevel: true});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_WHEEL,
          {start: 182, end: 200});
      ChromeTestUtils.addFrameEvent(model,
          {start: 199, end: 200, isTopLevel: true});
    };
    verifier.expectedUEs = [
      {title: 'MouseWheel Response', start: 0, end: 20, eventCount: 1},
      {title: 'MouseWheel Animation', start: 20, end: 75,
        eventCount: 4},
      {title: 'Idle', start: 75, end: 100, eventCount: 0},
      {title: 'MouseWheel Response', start: 100, end: 150,
        eventCount: 1},
      {title: 'MouseWheel Response', start: 141, end: 191,
        eventCount: 1},
      {title: 'MouseWheel Response', start: 182, end: 200,
        eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 20, expectations: [verifier.expectedUEs[0]]},
      {start: 20, end: 75, expectations: [verifier.expectedUEs[1]]},
      {start: 75, end: 100, expectations: [verifier.expectedUEs[2]]},
      {start: 100, end: 141, expectations: [verifier.expectedUEs[3]]},
      {start: 141, end: 150, expectations: [
        verifier.expectedUEs[3],
        verifier.expectedUEs[4],
      ]},
      {start: 150, end: 182, expectations: [verifier.expectedUEs[4]]},
      {start: 182, end: 191, expectations: [
        verifier.expectedUEs[4],
        verifier.expectedUEs[5],
      ]},
      {start: 191, end: 200, expectations: [verifier.expectedUEs[5]]},
    ];
    verifier.verify();
  });

  test('mouseDownUpResponse', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_DOWN,
          {start: 0, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_UP,
          {start: 200, end: 210});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 200, eventCount: 0},
      {title: 'Mouse Response', start: 200, end: 210, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 200, expectations: [verifier.expectedUEs[0]]},
      {start: 200, end: 210, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('ignoreLoneMouseMoves', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.MOUSE_MOVE,
          {start: 0, end: 100});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 100, eventCount: 0}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]}
    ];
    verifier.verify();
  });

  test('mouseDrags', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_DOWN, {start: 0, end: 100});
      let mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 200, end: 215});
      addFrameEventForInput(model, mouseMove);
      mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 210, end: 220});
      addFrameEventForInput(model, mouseMove);
      mouseMove = ChromeTestUtils.addInputEvent(
          model, INPUT_TYPE.MOUSE_MOVE, {start: 221, end: 240});
      addFrameEventForInput(model, mouseMove);
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 200, eventCount: 0},
      {title: 'Mouse Response', start: 200, end: 215, eventCount: 4},
      {title: 'Mouse Animation', start: 215, end: 240, eventCount: 6}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 200, expectations: [verifier.expectedUEs[0]]},
      {start: 200, end: 215, expectations: [verifier.expectedUEs[1]]},
      {start: 215, end: 240, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('twoScrollsNoFling', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 0, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 20, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 40, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 60, end: 150});
      ChromeTestUtils.addFrameEvent(model, {start: 149, end: 150});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 70, end: 150});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_END,
          {start: 80, end: 150});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 300, end: 400});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 320, end: 400});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 330, end: 450});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 340, end: 450});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 350, end: 500});
      ChromeTestUtils.addFrameEvent(model, {start: 499, end: 500});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_END,
          {start: 360, end: 500});
    };
    verifier.expectedUEs = [
      {title: 'Scroll Response', start: 0, end: 100, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Scroll Animation', start: 100, end: 150, eventCount: 5},
      {title: 'Idle', start: 150, end: 300, eventCount: 0},
      {title: 'Scroll Response', start: 300, end: 400, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Scroll Animation', start: 400, end: 500, eventCount: 5}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 150, expectations: [verifier.expectedUEs[1]]},
      {start: 150, end: 300, expectations: [verifier.expectedUEs[2]]},
      {start: 300, end: 400, expectations: [verifier.expectedUEs[3]]},
      {start: 400, end: 500, expectations: [verifier.expectedUEs[4]]},
    ];
    verifier.verify();
  });

  test('webGLAnimations_oneAnimation', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'DrawingBuffer::prepareMailbox', start: 0, end: 2}));
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 18, end: 19});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 20, end: 22});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 38, end: 39});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 40, end: 42});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 58, end: 59});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 60, end: 62});
    };
    verifier.expectedUEs = [
      {title: 'WebGL Animation', start: 0, end: 62, eventCount: 4},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 62, expectations: [verifier.expectedUEs[0]]}
    ];
    verifier.verify();
  });

  test('webGLAnimations_twoAnimations', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'DrawingBuffer::prepareMailbox', start: 0, end: 2}));
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 18, end: 19});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 20, end: 22});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 38, end: 39});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 40, end: 42});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 58, end: 59});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 60, end: 62});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 218, end: 19});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 220, end: 222});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 238, end: 39});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 240, end: 242});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 258, end: 59});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 260, end: 262});
    };
    verifier.expectedUEs = [
      {title: 'WebGL Animation', start: 0, end: 62, eventCount: 4},
      {title: 'Idle', start: 62, end: 220, eventCount: 0},
      {title: 'WebGL Animation', start: 220, end: 262, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 62, expectations: [verifier.expectedUEs[0]]},
      {start: 62, end: 220, expectations: [verifier.expectedUEs[1]]},
      {start: 220, end: 262, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('webGLAnimations_oneWithAnimationEventsOneWithout', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'DrawingBuffer::prepareMailbox', start: 0, end: 2}));
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 18, end: 19});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 20, end: 22});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 38, end: 39});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 40, end: 42});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 58, end: 59});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 60, end: 62});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 220, end: 222});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 240, end: 242});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 260, end: 262});
    };
    verifier.expectedUEs = [
      {title: 'WebGL Animation', start: 0, end: 62, eventCount: 4},
      {title: 'Idle', start: 62, end: 262, eventCount: 0},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 62, expectations: [verifier.expectedUEs[0]]},
      {start: 62, end: 262, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('webGLAnimations_noAnimationEvents', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'DrawingBuffer::prepareMailbox', start: 0, end: 2}));
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 20, end: 22});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 40, end: 42});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 60, end: 62});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 220, end: 222});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 240, end: 242});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'DrawingBuffer::prepareMailbox', start: 260, end: 262});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 262, eventCount: 0},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 262, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('webGLAnimations_animationEventsOnly', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'PageAnimator::serviceScriptedAnimations',
            start: 0, end: 2}));
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 20, end: 22});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 40, end: 42});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 42, eventCount: 0},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 42, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('webGLAnimations_oneEvent', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'DrawingBuffer::prepareMailbox', start: 0, end: 2}));
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'PageAnimator::serviceScriptedAnimations',
        start: 4, end: 6});
    };
    verifier.expectedUEs = [
      {title: 'WebGL Animation', start: 0, end: 2, eventCount: 1},
      {title: 'Idle', start: 2, end: 6, eventCount: 0},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 2, expectations: [verifier.expectedUEs[0]]},
      {start: 2, end: 6, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('cssAnimations', function() {
    // CSS Animations happen on the renderer process, not the browser process.
    // They are merged if they overlap.
    // They are merged with other kinds of animations.
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(
          {title: 'Animation', start: 0, end: 130, isTopLevel: true}));
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20});
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'Animation', start: 131, end: 200, isTopLevel: true});
      ChromeTestUtils.addFrameEvent(model, {start: 150, end: 160});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 150, end: 180});
      ChromeTestUtils.addFrameEvent(model, {start: 290, end: 300});
    };
    verifier.expectedUEs = [
      {title: 'CSS Animation', start: 0, end: 200, eventCount: 4},
      {title: 'Fling Animation', start: 150, end: 300, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 150, expectations: [verifier.expectedUEs[0]]},
      {start: 150, end: 200, expectations: [
        verifier.expectedUEs[0],
        verifier.expectedUEs[1],
      ]},
      {start: 200, end: 300, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('cssAnimationStatesRunningAtEnd', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      // If a top-level Animation async slice has state-change instant
      // events and the last one is a "running" event, then it will run
      // to the end of the top level event.
      const animationA = tr.c.TestUtils.newAsyncSliceEx(
          {title: 'Animation', start: 0, end: 500, isTopLevel: true});
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 100, args: {data: {state: 'running'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 200, args: {data: {state: 'idle'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 300, args: {data: {state: 'running'}}}));
      model.rendererMain.asyncSliceGroup.push(animationA);
      ChromeTestUtils.addFrameEvent(model, {start: 50, end: 60});
      ChromeTestUtils.addFrameEvent(model, {start: 150, end: 160});
      ChromeTestUtils.addFrameEvent(model, {start: 250, end: 260});
      ChromeTestUtils.addFrameEvent(model, {start: 350, end: 360});
      ChromeTestUtils.addFrameEvent(model, {start: 450, end: 460});
      // We include a frame event off the end of the top level animation slice
      // so we can test that it correctly stops the AnimationExpectation
      // at the end of the top-level event, not tne end of the whole trace,
      ChromeTestUtils.addFrameEvent(model, {start: 1050, end: 1060});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 100, eventCount: 1},
      {title: 'CSS Animation', start: 100, end: 200, eventCount: 5},
      {title: 'Idle', start: 200, end: 300, eventCount: 1},
      {title: 'CSS Animation', start: 300, end: 500, eventCount: 6},
      {title: 'Idle', start: 500, end: 1060, eventCount: 1},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 200, expectations: [verifier.expectedUEs[1]]},
      {start: 200, end: 300, expectations: [verifier.expectedUEs[2]]},
      {start: 300, end: 500, expectations: [verifier.expectedUEs[3]]},
      {start: 500, end: 1060, expectations: [verifier.expectedUEs[4]]},
    ];
    verifier.verify();
  });

  test('cssAnimationStates', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      // If a top-level Animation async slice does not have state-change instant
      // subSlices, then assume that the animation was running throughout the
      // async slice.
      ChromeTestUtils.addEvent(model.rendererMain, {
        title: 'Animation', start: 181, end: 250, isTopLevel: true});
      ChromeTestUtils.addFrameEvent(model, {start: 200, end: 240});

      // Animation ranges should be merged if there is less than 32ms dead time
      // between them.

      // If a top-level Animation async slice has state-change instant events,
      // then run a state machine to find the time ranges when the animation was
      // actually running.

      // This animation was running from 10-40 and 50-60.
      const animationA = tr.c.TestUtils.newAsyncSliceEx(
          {title: 'Animation', start: 50, end: 500, isTopLevel: true});
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 71, args: {data: {state: 'running'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 104, args: {data: {state: 'pending'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 137, args: {data: {state: 'running'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 150, args: {data: {state: 'paused'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 281, args: {data: {state: 'running'}}}));
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 350, args: {data: {state: 'idle'}}}));
      model.rendererMain.asyncSliceGroup.push(animationA);
      ChromeTestUtils.addFrameEvent(model, {start: 80, end: 90});
      ChromeTestUtils.addFrameEvent(model, {start: 290, end: 300});

      // An animation without a frame event isn't really an animation.
      const animationC = tr.c.TestUtils.newAsyncSliceEx(
          {title: 'Animation', start: 350, end: 382, isTopLevel: true});
      model.rendererMain.asyncSliceGroup.push(animationC);
      animationA.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 350, args: {data: {state: 'idle'}}}));

      // This animation was running from model.bounds.min-50 and
      // 70-model.bounds.max.
      const animationB = tr.c.TestUtils.newAsyncSliceEx(
          {title: 'Animation', start: 0, end: 500, isTopLevel: true});
      animationB.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 40, args: {data: {state: 'finished'}}}));
      animationB.subSlices.push(tr.c.TestUtils.newInstantEvent(
          {title: 'Animation', start: 382, args: {data: {state: 'running'}}}));
      model.rendererMain.asyncSliceGroup.push(animationB);
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20});
      ChromeTestUtils.addFrameEvent(model, {start: 390, end: 400});
    };
    verifier.expectedUEs = [
      {title: 'CSS Animation', start: 0, end: 350, eventCount: 16},
      {title: 'Idle', start: 350, end: 382, eventCount: 0},
      {title: 'CSS Animation', start: 382, end: 500, eventCount: 4},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 350, expectations: [verifier.expectedUEs[0]]},
      {start: 350, end: 382, expectations: [verifier.expectedUEs[1]]},
      {start: 382, end: 500, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('flingThatIsntstopped', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 32, end: 100});
      ChromeTestUtils.addFlingAnimationEvent(model, {start: 38, end: 200});
      ChromeTestUtils.addFrameEvent(model, {start: 199, end: 200});
      ChromeTestUtils.addFrameEvent(model, {start: 290, end: 300});
    };
    verifier.expectedUEs = [
      {title: 'Fling Animation', start: 32, end: 200, eventCount: 3},
      {title: 'Idle', start: 200, end: 300, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 32, end: 200, expectations: [verifier.expectedUEs[0]]},
      {start: 200, end: 300, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('flingThatIsStopped', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 32, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_CANCEL,
          {start: 105, end: 150});
      ChromeTestUtils.addFrameEvent(model, {start: 104, end: 105});
      ChromeTestUtils.addFrameEvent(model, {start: 149, end: 150});
    };
    verifier.expectedUEs = [
      {title: 'Fling Animation', start: 32, end: 105, eventCount: 3},
      {title: 'Idle', start: 105, end: 150, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 32, end: 105, expectations: [verifier.expectedUEs[0]]},
      {start: 105, end: 150, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('flingFling', function() {
    // measurmt-traces/mobile/facebook_obama_scroll_dialog_box.html
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 0, end: 30});
      ChromeTestUtils.addFrameEvent(model, {start: 40, end: 41});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 100, end: 130});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_CANCEL,
          {start: 100, end: 130});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 110, end: 140});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 170, end: 180});
      ChromeTestUtils.addFrameEvent(model, {start: 150, end: 151});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 200, end: 210});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 200, end: 220});
      ChromeTestUtils.addFrameEvent(model, {start: 230, end: 240});
    };
    verifier.expectedUEs = [
      {title: 'Fling Animation', start: 0, end: 100, eventCount: 3},
      {title: 'Touch Response', start: 100, end: 140, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Touch Animation', start: 140, end: 210, eventCount: 3},
      {title: 'Fling Animation', start: 200, end: 240, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 140, expectations: [verifier.expectedUEs[1]]},
      {start: 140, end: 200, expectations: [verifier.expectedUEs[2]]},
      {start: 200, end: 210, expectations: [
        verifier.expectedUEs[2],
        verifier.expectedUEs[3],
      ]},
      {start: 210, end: 240, expectations: [verifier.expectedUEs[3]]},
    ];
    verifier.verify();
  });

  test('load', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      model.rendererProcess.objects.addSnapshot('ptr', 'loading', 'FrameLoader',
        25, { isOutermostMainFrame: true, isLoadingMainFrame: true,
              frame: {id_ref: '0xdeadbeef'},
              documentLoaderURL: 'http://example.com'});
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'navigationStart',
        start: 0,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newSliceEx({
        cat: 'disabled-by-default-network',
        title: 'ResourceLoad',
        start: 0,
        duration: 5.0,
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstContentfulPaint',
        start: 20,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstMeaningfulPaintCandidate',
        start: 30,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'domContentLoadedEventEnd',
        start: 40,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'toplevel',
        title: tr.e.chrome.SCHEDULER_TOP_LEVEL_TASK_TITLE,
        start: 50,
        duration: 300,
      }));
      ChromeTestUtils.addFrameEvent(model, {start: 7000, end: 7130});
    };

    verifier.expectedUEs = [
      {title: 'Successful Load', start: 0, end: 350, eventCount: 5},
      {title: 'Idle', start: 350, end: 7130, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 350, expectations: [verifier.expectedUEs[0]]},
      {start: 350, end: 7130, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('loadStartup', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addRenderingEvent(model, {start: 2, end: 3});
      ChromeTestUtils.addCreateThreadsEvent(model, {start: 5, end: 10});
      // findStartupExpectations() should ignore subsequent CreateThreads
      // events.
      ChromeTestUtils.addCreateThreadsEvent(model, {start: 25, end: 30});
      ChromeTestUtils.addFrameEvent(model, {start: 11, end: 20});
    };
    verifier.expectedUEs = [
      {title: 'Startup', start: 2, end: 20, eventCount: 3},
      {title: 'Idle', start: 20, end: 30, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 2, end: 20, expectations: [verifier.expectedUEs[0]]},
      {start: 20, end: 30, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('totalIdle', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 10, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 10, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('multipleIdles', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
      model.rendererProcess.objects.addSnapshot('ptr', 'loading', 'FrameLoader',
        25, { isOutermostMainFrame: true, isLoadingMainFrame: true,
              frame: {id_ref: '0xdeadbeef'},
              documentLoaderURL: 'http://example.com'});
      ChromeTestUtils.addFrameEvent(model, {start: 10, end: 20});
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'navigationStart',
        start: 10,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.asyncSliceGroup.push(tr.c.TestUtils.newSliceEx({
        cat: 'disabled-by-default-network',
        title: 'ResourceLoad',
        start: 10,
        duration: 5.0,
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstContentfulPaint',
        start: 20,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'loading',
        title: 'firstMeaningfulPaintCandidate',
        start: 30,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: 'domContentLoadedEventEnd',
        start: 40,
        duration: 0.0,
        args: {frame: '0xdeadbeef'}
      }));
      model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'toplevel',
        title: tr.e.chrome.SCHEDULER_TOP_LEVEL_TASK_TITLE,
        start: 50,
        duration: 300,
      }));
      ChromeTestUtils.addFrameEvent(model, {start: 7000, end: 7130});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 10, eventCount: 1},
      {title: 'Successful Load', start: 10, end: 350, eventCount: 6},
      {title: 'Idle', start: 350, end: 7130, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 10, expectations: [verifier.expectedUEs[0]]},
      {start: 10, end: 350, expectations: [verifier.expectedUEs[1]]},
      {start: 350, end: 7130, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('touchStartTouchEndTap', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 0, end: 10});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 200, end: 210});
    };
    verifier.expectedUEs = [
      {title: 'Touch Response', start: 0, end: 210, eventCount: 2,
        isAnimationBegin: true}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 210, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('touchMoveResponseAnimation', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 0, end: 10});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 50, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 70, end: 150});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 200, end: 300});
      ChromeTestUtils.addFrameEvent(model, {start: 299, end: 300});
    };
    verifier.expectedUEs = [
      {title: 'Touch Response', start: 0, end: 100, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Touch Animation', start: 100, end: 300, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 300, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  test('tapEvents', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP,
          {start: 0, end: 50});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_DOWN,
          {start: 300, end: 310});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP,
          {start: 320, end: 350});
    };
    verifier.expectedUEs = [
      {title: 'Tap Response', start: 0, end: 50, eventCount: 1},
      {title: 'Idle', start: 50, end: 300, eventCount: 0},
      {title: 'Tap Response', start: 300, end: 350, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 50, expectations: [verifier.expectedUEs[0]]},
      {start: 50, end: 300, expectations: [verifier.expectedUEs[1]]},
      {start: 300, end: 350, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('tapAndTapCancelResponses', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_DOWN,
          {start: 0, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_CANCEL,
          {start: 300, end: 350});
    };
    verifier.expectedUEs = [
      {title: 'Tap Response', start: 0, end: 100, eventCount: 1},
      {title: 'Idle', start: 100, end: 300, eventCount: 0},
      {title: 'Tap Response', start: 300, end: 350, eventCount: 1}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 300, expectations: [verifier.expectedUEs[1]]},
      {start: 300, end: 350, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('tapCancelResponse', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_DOWN,
          {start: 0, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_CANCEL,
          {start: 150, end: 200});
    };
    verifier.expectedUEs = [
      {title: 'Tap Response', start: 0, end: 200, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 200, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('pinchResponseAnimation', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_BEGIN,
          {start: 100, end: 150});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_UPDATE,
          {start: 130, end: 160});
      ChromeTestUtils.addFrameEvent(model, {start: 159, end: 160});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_UPDATE,
          {start: 140, end: 200});
      ChromeTestUtils.addFrameEvent(model, {start: 199, end: 200});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_UPDATE,
          {start: 150, end: 205});
      ChromeTestUtils.addFrameEvent(model, {start: 204, end: 205});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_UPDATE,
          {start: 210, end: 220});
      ChromeTestUtils.addFrameEvent(model, {start: 219, end: 220});
      // pause > 200ms
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_UPDATE,
          {start: 421, end: 470});
      ChromeTestUtils.addFrameEvent(model, {start: 469, end: 470});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_END,
          {start: 460, end: 500});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 100, eventCount: 1},
      {title: 'Pinch Response', start: 100, end: 160, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Pinch Animation', start: 160, end: 220, eventCount: 6},
      {title: 'Idle', start: 220, end: 421, eventCount: 0},
      {title: 'Pinch Animation', start: 421, end: 500, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
      {start: 100, end: 160, expectations: [verifier.expectedUEs[1]]},
      {start: 160, end: 220, expectations: [verifier.expectedUEs[2]]},
      {start: 220, end: 421, expectations: [verifier.expectedUEs[3]]},
      {start: 421, end: 500, expectations: [verifier.expectedUEs[4]]},
    ];
    verifier.verify();
  });

  test('tapThenScroll', function() {
    // measurmt-traces/mobile/google_io_instrument_strumming.json
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 0, end: 20});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 40, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 50, end: 120});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 80, end: 150});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 180, end: 200});
      ChromeTestUtils.addFrameEvent(model, {start: 199, end: 200});
    };
    verifier.expectedUEs = [
      {title: 'Touch Response', start: 0, end: 100, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Touch Response', start: 50, end: 150, eventCount: 2,
        isAnimationBegin: true},
      {title: 'Touch Animation', start: 150, end: 200, eventCount: 2}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 50, expectations: [verifier.expectedUEs[0]]},
      {start: 50, end: 100, expectations: [
        verifier.expectedUEs[0],
        verifier.expectedUEs[1],
      ]},
      {start: 100, end: 150, expectations: [verifier.expectedUEs[1]]},
      {start: 150, end: 200, expectations: [verifier.expectedUEs[2]]},
    ];
    verifier.verify();
  });

  test('pinchFlingTapTouchEventsOverlap', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addFrameEvent(model, {start: 0, end: 10});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 20, end: 50});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_DOWN,
          {start: 20, end: 30});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_CANCEL,
          {start: 20, end: 50});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 60, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 60, end: 110});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_BEGIN,
          {start: 60, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_CANCEL,
          {start: 65, end: 75});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 70, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.PINCH_UPDATE,
          {start: 70, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 75, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 80, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 85, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 75, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 150, end: 200});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 150, end: 200});
      ChromeTestUtils.addFrameEvent(model, {start: 199, end: 200});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 180, end: 210});
      ChromeTestUtils.addFrameEvent(model, {start: 209, end: 210});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 190, end: 210});
      ChromeTestUtils.addFrameEvent(model, {start: 215, end: 220});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 20, eventCount: 1},
      {title: 'Pinch Response', start: 20, end: 110,
        eventCount: 9, isAnimationBegin: true},
      {title: 'Scroll Animation', start: 110, end: 210,
        eventCount: 7},
      {title: 'Fling Animation', start: 180, end: 220, eventCount: 4}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 20, expectations: [verifier.expectedUEs[0]]},
      {start: 20, end: 110, expectations: [verifier.expectedUEs[1]]},
      {start: 110, end: 180, expectations: [verifier.expectedUEs[2]]},
      {start: 180, end: 210, expectations: [
        verifier.expectedUEs[2],
        verifier.expectedUEs[3],
      ]},
      {start: 210, end: 220, expectations: [verifier.expectedUEs[3]]},
    ];
    verifier.verify();
  });

  test('scrollThenFling', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 0, end: 40});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 50, end: 100});
      ChromeTestUtils.addFrameEvent(model, {start: 99, end: 100});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 80, end: 100});
      ChromeTestUtils.addFrameEvent(model, {start: 190, end: 200});
    };
    verifier.expectedUEs = [
      {title: 'Scroll Animation', start: 0, end: 100, eventCount: 3},
      {title: 'Fling Animation', start: 80, end: 200, eventCount: 3}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 80, expectations: [verifier.expectedUEs[0]]},
      {start: 80, end: 100, expectations: [
        verifier.expectedUEs[0],
        verifier.expectedUEs[1],
      ]},
      {start: 100, end: 200, expectations: [verifier.expectedUEs[1]]},
    ];
    verifier.verify();
  });

  /*
    This test was generated from
    /test_data/measurmt-traces/mobile/fling_HN_to_rest.json
   */
  test('flingHNToRest', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addEvent(model.browserMain,
          {title: 'model start', start: 0, end: 1});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_START,
          {start: 1274, end: 1297});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_DOWN,
          {start: 1274, end: 1305});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1343, end: 1350});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1359, end: 1366});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP_CANCEL,
          {start: 1359, end: 1366});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 1359, end: 1367});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1359, end: 1387});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1375, end: 1385});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1375, end: 1416});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1389, end: 1404});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1389, end: 1429});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1405, end: 1418});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1405, end: 1449});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 1419, end: 1432});
      ChromeTestUtils.addFrameEvent(model, {start: 1431, end: 1432});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 1419, end: 1474});
      ChromeTestUtils.addFrameEvent(model, {start: 1473, end: 1474});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_END,
          {start: 1427, end: 1435});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.FLING_START,
          {start: 1427, end: 1474});
      ChromeTestUtils.addFlingAnimationEvent(model, {start: 1440, end: 2300});
      ChromeTestUtils.addEvent(model.browserMain,
          {title: 'model end', start: 3184, end: 3185});
    };
    verifier.expectedUEs = [
      {title: 'Idle', start: 0, end: 1274, eventCount: 0},
      {title: 'Scroll Response', start: 1274, end: 1387,
        eventCount: 6, isAnimationBegin: true},
      {title: 'Scroll Animation', start: 1387, end: 1474,
        eventCount: 12},
      {title: 'Fling Animation', start: 1427, end: 2300,
        eventCount: 4},
      {title: 'Idle', start: 2300, end: 3185, eventCount: 0}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 1274, expectations: [verifier.expectedUEs[0]]},
      {start: 1274, end: 1387, expectations: [verifier.expectedUEs[1]]},
      {start: 1387, end: 1427, expectations: [verifier.expectedUEs[2]]},
      {start: 1427, end: 1474, expectations: [
        verifier.expectedUEs[2],
        verifier.expectedUEs[3],
      ]},
      {start: 1474, end: 2300, expectations: [verifier.expectedUEs[3]]},
      {start: 2300, end: 3185, expectations: [verifier.expectedUEs[4]]},
    ];
    verifier.verify();
  });

  test('TapResponseOverlappingTouchAnimation', function() {
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 0, end: 10});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 5, end: 15});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TOUCH_MOVE,
          {start: 10, end: 20});
      ChromeTestUtils.addFrameEvent(model, {start: 19, end: 20});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.TAP,
          {start: 15, end: 100});
    };
    verifier.expectedUEs = [
      {title: 'Tap Response', start: 0, end: 100,
        eventCount: 5}
    ];
    verifier.expectedSegments = [
      {start: 0, end: 100, expectations: [verifier.expectedUEs[0]]},
    ];
    verifier.verify();
  });

  test('responseFramesNotInScrollAnimation', function() {
    // fixResponseAnimationStarts in find_input_expectations moves the start of
    // the Scroll Animation and needs to remove frame events that now lie
    // out of the Scroll Animation's interval
    const verifier = new UserExpectationVerifier();
    verifier.customizeModelCallback = function(model) {
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_BEGIN,
          {start: 0, end: 20});
      ChromeTestUtils.addFrameEvent(model, {start: 5, end: 6});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 20, end: 40});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_UPDATE,
          {start: 20, end: 210});
      // don't associate the follwing event to the Scroll Animation
      ChromeTestUtils.addFrameEvent(model, {start: 25, end: 26});
      ChromeTestUtils.addFrameEvent(model, {start: 190, end: 191});
      ChromeTestUtils.addInputEvent(model, INPUT_TYPE.SCROLL_END,
          {start: 200, end: 250});
    };
    verifier.expectedUEs = [
      {title: 'Scroll Response', start: 0, end: 40,
        eventCount: 2, isAnimationBegin: true},
      {title: 'Scroll Animation', start: 40, end: 250, eventCount: 3},
    ];
    verifier.expectedSegments = [
      {start: 0, end: 40, expectations: [verifier.expectedUEs[0]]},
      {start: 40, end: 250, expectations: [verifier.expectedUEs[1]]}
    ];
    verifier.verify();
  });
});
</script>
